今天要繼續看 jinja 的使用,昨天的內容比較接近一般的變數存取、if、for 的使用,而今天的內容則是類似自定義函數。
先來看個範例。請注意 index.html
和 msg.jinja
都要放在昨天提到的 templates/
裡面。
app.py
from flask import Flask, render_template, flash
app = Flask(__name__)
@app.route("/")
def index_page():
users = ["Mary", "Cat", "Meow", "Harry"]
flash("meow", category="cat")
return render_template("index.html", users=users)
app.config["SECRET_KEY"] = "rc498mt6848"
app.run(host="127.0.0.1", port=8080, debug=True)
index.html
{% from 'msg.jinja' import display_msg %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
{{ display_msg(get_flashed_messages(with_categories=True)) }}
<h1>User list</h1>
<ul>
{% for user in users %}
<li>{{ user }} ({{ loop.index }})</li>
{% endfor %}
</ul>
</body>
</html>
msg.jinja
{% macro display_msg(messages) %}
<div id="messages">
{% if messages %}
{% for category, message in messages %}
<div class="{{ category }}">
<span>{{ message }}</span>
</div>
{% endfor %}
{% endif %}
</div>
{% endmacro %}
這次的檔案比較多,也比較繁雜,我們一個一個慢慢看。
app.py
基本上就是昨天用來解釋 for 的範例,但是新加入了一個叫做 flash
的函式 ,它的用途是把後端的資料送到前端顯示。比如說可能使用者在註冊帳號的時候,使用者名稱已經被用過了,那後端就可以發一條警告訊息到前端通知使用者這個名稱被使用了。雖然這裡說是前端,但基本上它也是由 jinja 去跑的,所以需要用 render_template
。在他的後面我們加了一個 category
的參數,這是選用,它的功能就是放一個標籤給這條訊息,讓前端可以因應,例如說上面的名稱已被使用可能就是 warning
,而註冊成功可能就是 success
之類的,在下面我們會用到它。
這裡還有一個需要注意的是,有一行 app.config["SECRET_KEY"] = "rc498mt6848"
,app.config
是這個 app
的設定檔,未來會有更舒服的方式去設定他,而 SECRET_KEY
是這個 app 的 secret key,會用來幫忙處理一些 session 的東西,像是此處的 flash
、登入的 session、未來會提到的 csrf_token 等等都會用到它,而有了它也可以反推前述的東西,因此不能外流,此處只是隨便亂打,而且在開發模式,所以是沒有關係的。
如果你在思考這個 flash
的東西到底去哪裡了,可以看看 session
,但一樣要記得先 import (from flask import session
)。應該可以看到類似 <SecureCookieSession {'_flashes': [('meow', 'meow')]}>
的內容。這個訊息還有一個特性是,在被前端使用過之後就會自動從 session
中消失,所以同樣的訊息不會一直卡著。
接下來看到 index.html
,他在最一開始就 import,但是這並不是 python 的 import,他 import 的是一個 jinja 的檔案,而這個檔案在下方,我們等等就會看到。在 body
裡面,我們呼叫剛剛引入的 display_msg
,並將 get_flashed_messages(with_categories=True)
作為其引數。這個函式就是用來抓 session
裡面有哪些 flash
的訊息,後面的 with_categories
是說明要不要加上 category
,預設是 False
。他的表示方式是一個 list,以此處為例,有 with_categories=True
的結果會是 [("cat", "meow")]
、沒有打或是設為 False
會得到 ["meow"]
。
最後看到 msg.jinja
,他就像是一個 module,裡面有一個函式可以給別人引入。首先,就如同 if 和 for,他也需要 endmacro
,接下來我們可以看到他在 jinja 裡面跑著非常接近 python 的語法,也可以發現剛剛設定的 category
被當成 class
來用。
在前面講存取變數的時候,可能有人會好奇我們該如何設變數,因為之後用不太到,所以沒有放在前面講,我自己覺得不太會用到的原因是因為我們會用到的變數都已經在 render_template
中設定好了,剩下可能會用到的變數大概也只是 for 的時候會用到的區域變數,所以我個人覺得這個部分不太會使用,等等也不會放上範例。但我自己也真的有用過,像是想要把一些設定檔交給 jinja 去對照,那就會先把設定檔 import 進模板 (如何 import 等等會提到),然後給他一個變數名稱作為簡寫之類的。
標題寫著 set & with,代表這裡有兩種設定變數的方式,差異只在是全域變數還是區域變數而已。我們分開來說。
set
的用法是 {% set var=value %}
,非常簡單。with
就比較麻煩一點,他建立了一個 scope,也就是說他創造的是區域變數。他的用法是 {% with var=value %}
,但在這個 scope 結束的時候要 {% endwith %}
,和前面提到的類似。雖然我們是在 python 裡面用 jinja,但是在 jinja 中我們無法使用 python 的函式庫。如果真的要使用的話,我們需要一點小技巧。但我自己是認為,能不要用就不要用。
python 裡面有一個套件叫做 importlib
,我們可以在 render_template
後面加上 IMPORT=importlib.import_module
(import
在 jinja 裡面是保留字,所以不能用),然後在模板裡面就可以 set var=IMPORT('module')
,照著正常該怎麼用就可以了。
Configuration Handling - SECRET_KEY
How does the 'with' statement work in Flask (Jinja2)?
Whare are the difference between set and with in jinja
Import a Python module into a Jinja template?